package com.caishi.lkx.exam.api;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.caishi.lkx.common.AdminBaseApi;
import com.caishi.lkx.common.EncryptionApi;
import com.caishi.lkx.common.role.Role;
import com.caishi.lkx.common.role.Roles;
import com.caishi.lkx.exam.es.model.QuestionEs;
import com.caishi.lkx.exam.es.service.QuestionEsService;
import com.caishi.lkx.exam.model.QuestionModel;
import com.caishi.lkx.exam.service.ICategoryService;
import com.caishi.lkx.exam.service.IChapterService;
import com.caishi.lkx.exam.service.IPaperService;
import com.caishi.lkx.exam.service.IQuestionService;
import com.caishi.lkx.exam.service.impl.QuestionServiceImpl;
import com.caishi.lkx.exam.vo.QuestionVo;
import com.caishi.lkx.user.service.impl.ConfigServiceImpl;
import com.zzw.common.Result;
import com.zzw.common.annotation.RateLimiterApi;
import com.zzw.common.gateway.limit.RateLimiterApiTarget;
import com.zzw.common.qps.RejectStrategy;

import com.caishi.lkx.exam.ExamResultCode;
import com.caishi.lkx.exam.QuestionCollectionBlockItem;
import com.caishi.lkx.exam.dto.QuestionSourceDto;
import com.caishi.lkx.exam.ienum.type.QuestionDifficulty;
import com.caishi.lkx.exam.ienum.type.QuestionSourceType;
import com.caishi.lkx.exam.ienum.type.QuestionType;

import com.caishi.lkx.exam.records.RecordsCollectType;
import com.caishi.lkx.exam.records.RecordsGetDataService;

import com.caishi.lkx.exam.util.QuestionUtil;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.stream.Collectors;

import static com.zzw.common.utils.UUIDUtil.generateUUIDByTimestamp;

/**
 * @author by Jansia
 * @Date 2022/1/10 11:02
 * @Description 试题相关
 */
@Tag(name = "试题相关接口")
@Slf4j
@RestController
@RequestMapping("/exam/question")
public class QuestionApi extends AdminBaseApi<IQuestionService, QuestionModel, String> {

    @Resource
    private QuestionServiceImpl questionService;

    @Resource
    private IPaperService paperService;

    @Resource
    private IChapterService chapterService;

    @Resource
    private ICategoryService categoryService;
//    @Resource
//    private EsSearchUtil<QuestionModel, String, IQuestionService> esSearchUtil;
    @Resource
    private QuestionEsService questionEsService;

    @Resource
    private RecordsGetDataService recordsGetDataService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private ConfigServiceImpl configService;

    @Override
    public IQuestionService loadService() {
        return questionService;
    }

    @Override
    protected List<QuestionModel> listCover(List<QuestionModel> result, JSONObject otherParam) {
        return result.parallelStream().peek(q -> {
            if (QuestionType.combination.equals(q.getFirstType())) {
                List<QuestionModel> combineList = questionService.getCombineList(q.getId());
                q.setChildren(combineList);
            }
        }).toList();
    }
    @GetMapping("/sync-es/{id}")
    @Parameters({@Parameter(name = "id", description = "id", required = true),})
    public void syncEs(@PathVariable String id) {
         var question = questionService.getQuestionById(id);
         questionEsService.update(QuestionEs.modelTrans(question));
    }

    @GetMapping("/detail")
    @Parameters({@Parameter(name = "id", description = "id", required = true),})
    @Roles(Role.admin)
    public QuestionModel getQuestion(@RequestParam String id) {
        return questionService.getQuestionById(id);
    }

    /**
     * 分页查询试题
     *
     * @param page       页码
     * @param model      model
     * @param otherParam other
     * @return data
     */
    @Override
    @GetMapping("/page")
    @Roles({Role.admin})
    public IPage<QuestionModel> page(@ModelAttribute Page<QuestionModel> page, @ModelAttribute QuestionModel model, @NotNull JSONObject otherParam) {
        IPage<QuestionModel> data = new Page<>();
        List<QuestionCollectionBlockItem> content = new ArrayList<>();
        String resourceType = otherParam.getStr("resourceType", "");
        if (resourceType.equals("paper")) {
            String paperId = otherParam.getStr("paperId", "");
            if (StringUtil.isNotBlank(paperId)) {
                content = paperService.getById(paperId).getContent();
            } else {
                data = loadService().page(page, baseWrapper(model, otherParam));
            }
        } else if (resourceType.equals("chapter")) {
            String chapterId = otherParam.getStr("chapterId", "");
            if (StringUtil.isNotBlank(chapterId)) {
                content = chapterService.getById(chapterId).getContent();
            } else {
                data = loadService().page(page, baseWrapper(model, otherParam));
            }
        } else {
            data = loadService().page(page, baseWrapper(model, otherParam));
        }
        if (!content.isEmpty()) {
            List<String> listIds = new ArrayList<>();
            for (QuestionCollectionBlockItem item : content) {
                List<QuestionCollectionBlockItem.QuestionCollectionItem> qs = item.getQs();
                if (!qs.isEmpty()) {
                    List<String> qIds = qs.parallelStream().map(QuestionCollectionBlockItem.QuestionCollectionItem::getId).toList();
                    if (!qIds.isEmpty()) {
                        listIds.addAll(qIds);
                    }
                }
            }
            if (!listIds.isEmpty()) {
                data = loadService().page(page, baseWrapper(model, otherParam).in(QuestionModel::getId, listIds));
            }
        }
        data.setRecords(listCover(data.getRecords(), otherParam));
        return data;
    }

    @Roles({Role.admin})
    @Operation(summary = "新增试题")
    @PostMapping("/add-source-q")
    @Transactional(rollbackFor = Exception.class)
    @Parameters({@Parameter(name = "categoryId", description = "学科id"), @Parameter(name = "firstType", description = "大题型", required = true), @Parameter(name = "difficulty", description = "难度"), @Parameter(name = "stem", description = "题干", required = true), @Parameter(name = "answerDesc", description = "解析"), @Parameter(name = "quOption", description = "选项", required = true), @Parameter(name = "answer", description = "正确答案", required = true),})
    public QuestionModel addSourceC(@ModelAttribute QuestionModel questionModel) {
        if (questionModel.getDifficulty() == null) {
            questionModel.setDifficulty(QuestionDifficulty.normal);
        } else {
            questionModel.setDifficulty(questionModel.getDifficulty());
        }
        if (null == questionModel.getSourceType()) {
            questionModel.setSourceType(QuestionSourceType.importHand);
        }
        var r = addQuestion(questionModel, questionModel.getCategoryId()) != null ? questionModel : null;
//        if (r != null) {
//            questionEsService.update(questionService.convertQuestionEs(r));
//        }
        return r;
    }

    @GetMapping("/detail-child")
    @Operation(summary = "查看试题详情")
    @Parameters({@Parameter(name = "id", description = "id", required = true),})
    @Roles(Role.admin)
    public List<QuestionModel> getQuestionChild(@RequestParam String id) {
        return questionService.getCombineList(id);
    }

    /**
     * 公开、隐藏
     *
     * @param ids    ids
     * @param isHide 是否影藏
     * @return Boolean
     */
    @Roles({Role.admin})
    @PutMapping("/hide-list")
    public Boolean passList(@RequestParam(value = "ids") List<String> ids, Boolean isHide) {
        return questionService.isHideQuestion(ids, isHide);
    }

    @Roles({Role.admin})
    @Operation(summary = "用户删除试题")
    @DeleteMapping("/deleted-list")
    @Parameters({@Parameter(name = "ids", description = "试题id数组", required = true),})
    public Result<String> deletedList(@RequestParam(value = "ids") List<String> ids) {
        if (Boolean.FALSE.equals(questionService.canDelete(ids))) {
            return Result.fail(ExamResultCode.canNotDeleted);
        }
        Boolean delete = questionService.delete(ids);
        return Boolean.TRUE.equals(delete) ? Result.success("删除成功") : Result.fail(ExamResultCode.deletedQuestionFail);
    }

    @Operation(summary = "解密答案")
    @PostMapping("/user-answer-decrypt")
    @EncryptionApi
    @RateLimiterApi(namespace = "exam:safe:", maxRate = 2, recoveryCount = 2, target = RateLimiterApiTarget.user, rejectStrategy = RejectStrategy.wait, bean = "memoryRateLimiterBean")
    @Roles
    public Object questionAnswerDecrypt(@RequestParam String collectId, @RequestParam String qid, @RequestParam String answer) {
        String targetId = userContext.currentUserId();
        if (!userContext.loginStatus()) {
            Long groupId = userContext.currentLocalGroupId();
            targetId = null != groupId ? groupId.toString() : null;
        } else {
            if (collectId.matches("^[a-zA-Z]+$")) {
                collectId = targetId;
            }
        }
        return questionService.answerDecrypt(answer, targetId, collectId);
    }

    @GetMapping("/question-source")
    @Operation(summary = "获取那些试卷和章节包括当前试题")
    public QuestionSourceDto questionSource(@RequestParam String id) {
        return questionService.questionSource(id);
    }

    @Operation(summary = "单道试题做题")
    @PostMapping("/client-one-do")
    @EncryptionApi
    @Roles
    @Parameters({@Parameter(name = "id", description = "试题id"),
            @Parameter(name = "oneDoSource",description = "试题的来源 errorRank 错题排行|collectRank 收藏排行|myNewError 我的最新错题|myNewCollect 我的最新收藏|indexRecommend 首页推荐|erveryDay 每日一练| note 笔记"),
            @Parameter(name = "cid",description = "集合id,如果是章节收藏，就是章节id，试卷收藏就是试卷id,每日一练传每日一练记录id"),
            @Parameter(name = "view",description = "是否显示做题记录"),

    })
    @RateLimiterApi(namespace = "exam:oneq-safe", maxRate = 1000, recoveryCount = 10, target = RateLimiterApiTarget.ip, rejectStrategy = RejectStrategy.wait, bean = "memoryRateLimiterBean")
    public QuestionVo oneQuestionClientDetail(@RequestParam String id, @RequestParam String oneDoSource, @RequestParam String industryCode, @RequestParam String examId, @RequestParam Long categoryId, @RequestParam RecordsCollectType ct, boolean view, @RequestParam(required = false) String cid) {
        // errorRank 科目错题排行
        // collectRank 科目收藏排行
        // myNewError 我的最新错题
        // myNewCollect 我的最新收藏
        // indexRecommend 首页推荐
        // indexHotQue 考试热门试题
        // indexErrorRank 考试易错排行
        // indexCollectRank 考试收藏排行
        // note 笔记
        var uid = userContext.currentUserId();
        var res = questionService.clientGetQuestionById(id);
        if (res == null) return null;
        cid = ct.staticCid(cid);
        if (userContext.loginStatus() && StrUtil.isNotBlank(cid)) {
            var record = recordsGetDataService.getCollectInCollectDataResult(uid, industryCode, examId, categoryId, ct, cid);
            res.setCollect(record.get(id) != null);
        }
        //&& !"myNewCollect".equals(oneDoSource)
        if (view && userContext.loginStatus()  && StrUtil.isNotBlank(cid)) {
            var record = recordsGetDataService.getQuestionRecordsResult(uid, industryCode, examId, categoryId, ct, cid);
            if (null != res.getChildren() && !res.getChildren().isEmpty()) {
                res.getChildren().forEach(f -> {
                    res.setRecord(record.get(f.getId()));
                });
            }
            res.setRecord(record.get(id));
        }
        return res;
    }

    //======================pc端数据查询============================

//    @GetMapping("get-info-index")
//    @Operation(summary = "首页搜索试题")
//    @Parameters({@Parameter(name = "keyword", description = "搜索词")})
//    @Roles
//    public Page<QuestionModel> getInfoIndex(@ModelAttribute Page<QuestionModel> page, @RequestParam String keyword, @RequestParam(required = false) String examId, @RequestParam(required = false) String industryId) throws IOException {
//        //存试题的时候需要把examId 跟industryId 存入 es   或者 存试题表的categoryId，搜索是根据考试找到多个科目再来搜索。
//        if (Sensitive.isSensitive(keyword)) {
//            return page;
//        }
//        HashMap<String, Object> filterMap = new HashMap<>();
//        if (StringUtil.isNotBlank(examId)) {
//            filterMap.put("examId", examId);
//        }
//        if (StringUtil.isNotBlank(industryId)) {
//            filterMap.put("industryId", industryId);
//        }
//        Map<String, Object> resMap = esSearchUtil.esByConditions(page, keyword, Arrays.asList("answer", "answerDesc", "stem", "quOption"), filterMap, null, "vip-vers7_question", questionService, "quOption", "stem");
//        Map<String, Map<String, String>> replaceMap = (Map<String, Map<String, String>>) resMap.get("replaceMap");
//        Page<QuestionModel> resPage = (Page<QuestionModel>) resMap.get("page");
//        //获取敏感词汇
//        JSONArray sensitive = (JSONArray) configService.getValue("sensitive");
//
//        List<QuestionModel> collect = resPage.getRecords().stream().filter(Objects::nonNull).peek(r -> {
//            if (replaceMap.get(r.getId()) != null) {
//                sensitive.forEach(s -> {
//                    String sen = s.toString();
//                    StringBuilder filter = new StringBuilder();
//                    for (int i = 0; i < sen.length(); i++) {
//                        filter.append("*");
//                    }
//                    String option1 = replaceMap.get(r.getId()).get("quOption");
//                    String stem1 = replaceMap.get(r.getId()).get("stem");
//
//
//                    String quOption = StrUtil.isBlank(option1) ? option1 : option1.replaceAll(sen, filter.toString());
//                    String stem = StrUtil.isBlank(stem1) ? stem1 : stem1.replaceAll(sen, filter.toString());
//                    if (StringUtil.isNotBlank(stem)) {
//                        r.setStemHigh(coverJson(stem));
//                    }
//                    if (StringUtil.isNotBlank(quOption)) {
//                        r.setQuOptionHigh(coverJson(quOption));
//                    }
//                    if (r.getFirstType().equals(QuestionType.combination)) {
//                        r.setChildren(questionService.getCombineList(r.getId()).parallelStream().peek(p -> p.setType(questionService.questionTypeName(p.getTypeId(), p.getFirstType()))).toList());
//                    }
//
//                });
//
//            }
//            r.setQuestionSource(this.questionService.questionSource(r.getId()));
//            r.setType(questionService.questionTypeName(r.getTypeId(), r.getFirstType()));
//        }).collect(Collectors.toList());
//        resPage.setRecords(collect);
//        return resPage;
//    }


    private List<Map<String, Object>> coverJson(String str) {
        JSONArray jsonArray = JSONArray.parseArray(str);
        return (List<Map<String, Object>>) jsonArray.stream().map(obj -> {
            Map<String, Object> objectMap = (Map) JSON.toJSON(obj);
            return objectMap;
        }).collect(Collectors.toList());
    }

    /**
     * 添加试题
     *
     * @param questionModel 试题实体
     * @param categoryId    学科id
     * @return 结果
     */
    private String addQuestion(QuestionModel questionModel, Long categoryId) {
        String sid = generateUUIDByTimestamp();
        if (null != categoryId) {
            sid = categoryId + "_" + sid;
        }
        questionModel.setId(sid);
        questionModel.setIsHide(false);
        if (!questionModel.getFirstType().equals(QuestionType.combination)) {
            questionModel.setQualityLevel(questionService.additionQualityLevel(questionModel));
        }
        QuestionModel insertQue = questionService.insert(questionModel);
        return insertQue != null ? questionModel.getId() : null;
    }

    @PostMapping("/update")
    @Transactional(rollbackFor = Exception.class)
    public QuestionModel add(@RequestBody QuestionModel entity) {
        entity = super.add(entity);
        //questionEsService.update(questionService.convertQuestionEs(entity));
        return entity;
    }


    @PutMapping("/update")
    public QuestionModel update(@RequestBody QuestionModel entity) {
        QuestionModel questionModel = loadService().simpleUpdate(entity);
        //questionEsService.update(questionService.convertQuestionEs(questionModel));
        return questionModel;
    }


    @DeleteMapping("/delete")
    public Boolean delete(@RequestParam String id) {
        //questionEsService.delete(id);
        return loadService().delete(id);
    }

//    @Resource
//    private ElasticsearchRestTemplate elasticsearchRestTemplate;

//    @PostMapping("/syncES")
//    @Operation(description = "同步试卷到es")
//    public void syncES() {
//        var allCategoryIds = categoryService.selectList(categoryService.wrappers().select(CategoryModel::getId)).stream().map(BaseIntEntity::getId).toList();
//        var redisKey = "SYNC_QUESTION_ES";
//        var redisStatus = redisTemplate.opsForValue().get(redisKey);
//        if (redisStatus != null) {
//            throw new BizRuntimeException(String.format("同步中，已完成%s个科目。总%d个科目", redisStatus, allCategoryIds.size()));
//        }
//        redisTemplate.opsForValue().set(redisKey, 0, Duration.ofMinutes(20));
//        int i = 0;
//        for (var category : allCategoryIds) {
//            if (elasticsearchRestTemplate.count(new NativeSearchQueryBuilder().withQuery(QueryBuilders.termQuery("categoryId", category)).build(), QuestionEs.class) == 0) {
//                var qs = questionService.selectList(questionService.wrappers().eq(QuestionModel::getCategoryId, category));
//                for (var j = 0; j < qs.size(); j += 500) {
//                    var qs1 = qs.subList(j, Math.min(j + 500, qs.size()));
//                    questionEsService.batchUpdate(qs1.stream().map(questionService::convertQuestionEs).toList());
//                }
//            }
//            i++;
//            redisTemplate.opsForValue().set(redisKey, i, Duration.ofMinutes(20));
//        }
//    }

    @GetMapping("/question-info-list")
    @Parameters({@Parameter(name = "id", description = "id", required = true),})
    @Roles(Role.admin)
    public List<QuestionModel> getQuestionInfoList(String id) {
        QuestionModel model = questionService.getById(id);
        List<QuestionModel> questionModels = new ArrayList<>();
        if (QuestionUtil.questionIdIsCombinationChild(id)) {
            QuestionModel parentModel = questionService.getById(model.getSourceId());
            questionModels.add(parentModel);
        }
        questionModels.add(model);
        return questionModels;
    }


}
